#include "sched.h"
#include "i386.h"
#include "debug.h"

extern char *linea_log;
extern tss tarea_fork;
extern tss tsss[];
extern tss tarea_mondrian;
unsigned short tareas[CANT_TAREAS];
uint tarea_actual;

unsigned short *numeros = (unsigned short*)  0xB808C;

void inicializar_scheduler() {
    //inicializa estructuras del scheduler
    tarea_actual = -1;
    
    uint i=0;
    for(; i < CANT_TAREAS; i++){
        tareas[i] = 0;
        numeros[i] = 0x0000;
    }

    preparar_mondrian();
    lanzar_gusano();
    //~ lanzar_gusano();
    //~ lanzar_gusano();
}

uint proxima_tarea() {
    //~ breakpoint();
    
    
    uint i, res;
    if (tarea_actual == -1){
        //~ logText("IDLE");
        i = proximo_indice(0);
        if (i == -1){
             //~ logText("NO CAMBIO1");
             res = 0;
         }
        else{
             //~ logText("CAMBIO1");
             tarea_actual = i;
             res = tareas[i];
         }
    }else{
        //~ logText("WORM1");
        i = proximo_indice((tarea_actual + 1) % CANT_TAREAS);
        if (i == tarea_actual){
            //~ logText("NO CAMBIO2");
             res = 0;
        }else if(i == -1){
            tarea_actual = -1;
            //~ res = 0x28;             // tarea idle
            limpiar_pantalla();
            res = 0x88;                 // tarea mondrian
        }else {
            //~ logText("CAMBIO2");
            tarea_actual = i;
            res = tareas[i];
        }
    }
    
    return res;
    //~ if (rtr()== 0x28) return 0x38;
    //~ else return 0x28;

}

uint proximo_indice(unsigned short base){
    uint i = 0;
    while(i < CANT_TAREAS && tareas[(i + base) % CANT_TAREAS] == 0) i++;

    if (i == CANT_TAREAS) return -1;
    else return (i + base) % CANT_TAREAS;
}

uint slot_libre(){
    uint i = 0;
    while(i < CANT_TAREAS && tareas[i] != 0) i++;
    
    if (i == CANT_TAREAS) return -1;
    else return i;
}

uint crear_mapeo_tarea(uint codigo, uint datos, uint pila){
    //creo mapeo de memoria
    uint cr3_tarea = (uint)inicializar_dir_usuario();
    uint codigo_tarea = (uint)pagina_libre_usuario();
    uint datos_tarea = (uint)pagina_libre_usuario();
    uint pila_tarea = (uint)pagina_libre_usuario();
    
    //copia los datos y codigo de la tarea 
    copiar_pagina(codigo, codigo_tarea);
    copiar_pagina(datos, datos_tarea);
    
    if(pila != 0) copiar_pagina(pila, pila_tarea);

    //mapeo de la tarea
    mapear_pagina(0x0, cr3_tarea, codigo_tarea);
    mapear_pagina(0x20000, cr3_tarea, datos_tarea);
    mapear_pagina(0x30000, cr3_tarea, pila_tarea);
    
    return cr3_tarea;
}


void agregar_proceso(uint pid){
   numeros[pid] = 0x0F30 + pid;
   tareas[pid] = (pid+7) << 3;
}


void eliminar_proceso(uint pid){
   tareas[pid] = 0;
   numeros[pid] = 0x0000;
}

void lanzar_gusano(){
    //Busca espacio para la tarea
    uint indice = slot_libre();
    if (indice == -1) return;
    
    //crea el mapa de memoria de la tarea
    uint cr3_tarea = crear_mapeo_tarea(TASKWORMC, TASKWORMD, 0);
    cargar_tss(&(tsss[indice]), cr3_tarea, 0x30000, 0x0);
    
    //agrega al scheduler la tarea
    agregar_proceso(indice);
}


void duplicar_proceso(){

    //Busca espacio para la tarea
    uint indice = slot_libre();
    if (indice == -1) return;
    
    
    uint padre = tarea_fork.ptl;
    
    //borro los ultimos 3 bits para conseguir el indice
    padre = (padre >> 3) - 7;

    // buscar en el cr3 de la tarea padre las direcciones fisicas del codigo, datos y pila
    uint *codigo = buscar_entrada(0x0, tsss[padre].cr3);
    uint *datos =  buscar_entrada(0x20000, tsss[padre].cr3);
    uint *pila = buscar_entrada(0x30000, tsss[padre].cr3);
    
    // mapear esas direcciones a nuestro cr3 temporalmente
    // duplicar_mapeo_memoria(usar funcion crear_mapeo modificada para que tome la pila tambien) 
    uint cr3_tarea = crear_mapeo_tarea(codigo[0] & ~0xFFF, datos[0] & ~0xFFF, pila[0] & ~0xFFF);
    
    // duplicar la tss de la tarea padre
    duplicar_tss(&(tsss[padre]), &(tsss[indice]), cr3_tarea);
    
    
    // modificar el valor en eax de la tarea hija y padre
    tsss[padre].eax = 0;
    tsss[indice].eax = 1;
    
    //agrega al scheduler la tarea
    agregar_proceso(indice);
}

void preparar_mondrian(){
    
    //crea el mapa de memoria de la tarea
    uint cr3_tarea = crear_mapeo_tarea(TASKMONDRIANC, TASKMONDRIAND, 0);
    
    //pongo directamente las direcciones porque mapear_pagina alinea a 4k
    mapear_pagina(0x10101010, cr3_tarea, (uint) pagina_libre_usuario());    
    mapear_pagina(0x20202020, cr3_tarea, (uint) pagina_libre_usuario());    
    mapear_pagina(0x30303030, cr3_tarea, (uint) pagina_libre_usuario());        
    
    cargar_tss(&tarea_mondrian, cr3_tarea, 0x30000, 0x0);
    
}


void limpiar_pantalla(){
    uint *p = (uint*) 0xB8000;
    uint i = 1000-40;
    while (i >= 40){
        p[i--]=0;
    }
}


char egg[3] = {(char)0x12, (char) 0x22, (char) 0x22};
char msg1[29] = {0x48, 0x6f, 0x77, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x20, 0x6e, 0x69, 0x63, 0x65, 0x20, 0x63, 0x68, 0x65, 0x73, 0x73, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x3f, 0x0};
char msg[61] = {0x45,0x61,0x73,0x74,0x65,0x72,0x6e,0x20,0x45,0x47,0x47,0x21,0x21,0x21,0x21,0x20,0x43,0x6f,0x72,0x74,0x65,0x73,0x69,0x61,0x20,0x64,0x65,0x20,0x41,0x6e,0x67,0x69,0x75,0x6c,0x69,0x6e,0x6f,0x20,0x22,0x45,0x6c,0x20,0x63,0x68,0x69,0x6e,0x6f,0x22,0x20,0x43,0x75,0x62,0x72,0x65,0x70,0x69,0x6c,0x65,0x74,0x61,0x0};
uint egg_index = 0;

void chess(char code){
    uint *p = (uint*) 0xB8930;
    
    if (tarea_actual==-1 && p[0] == 0x0f2e0f79){
        linea_log = (char *) 0xB898A;
        logText(msg1);
    }

    if(egg_index != -1 && egg[egg_index] == code){
     if(egg_index == 2) {
         egg_index = -1;
         logText(msg);
         uint i= 0;
         for (; i < CANT_TAREAS; i++){
            eliminar_proceso(i);
         }
     }else{ egg_index++;}
    }
}

